use crate::{error::{Error, Result}};
use crate::db::model::prelude::*;
use crate::db::query_statement::prelude::*;
use crate::db::query_statement::convert_list_bind_value_to_args;
use crate::db::mysql::DbLink;
use crate::db::table::{TableExecutor};
use sqlx::mysql::{MySqlQueryResult, MySqlRow};
use sqlx::MySql;
use sqlx_mysql::MySqlArguments;
use sqlx_core::{executor, from_row::FromRow};
use std::{collections::HashSet, sync::Arc};

use super::table::ExecutorBase;


pub trait TableX:Sized{

    const TABLE_PREFIX_NAME:&'static str;
    const DATABASE_PREFIX_NAME:&'static str;

    const DATABASE_NAME:&'static str;

    const DATABASE_NUM:i16;
    const TABLE_NUM:i16;

    type Executor: TableExecutor;
    type Model: ModelQuery;

    fn get_table_names(&self) -> Result<Vec<String>>;
    fn get_sql_maker(&self) -> Result<SqlMakerRaw>;

    fn with_sql_maker_list(ids: Vec<i64>,maker:SqlMakerRaw) -> Self;


    fn with_sql_maker(id: i64,maker:SqlMakerRaw) -> Self{
        Self::with_sql_maker_list(vec![id], maker)
    }

    fn get_database_name(&self) -> &str{
        Self::DATABASE_NAME
    }

    fn get_database_index(id:i64) -> i16 {
        let index = id % Self::DATABASE_NUM as i64;
        index as i16
    }
    
    fn get_table_index(id:i64) -> i16{
        let index = id / Self::DATABASE_NUM as i64   %  Self::TABLE_NUM as i64;
        index as i16
    }

    fn get_table_name_by_index(id:i64) -> String{
        let databse_name = format!("{}_{}",Self::DATABASE_PREFIX_NAME,Self::get_database_index(id));
        let table_name = format!("{}_{}",Self::TABLE_PREFIX_NAME,Self::get_table_index(id));
        format!("`{}`.`{}`",databse_name,table_name)
    }

    fn get_db_handle(&self) -> Result<Arc<DbLink>>{
        let db_config = self.get_database_name();
        DbLink::instance(db_config)
    }

    fn get_real_sql(sql:String,table_name:String) -> String {
        let res = sql.replace(Self::Model::get_table_tpl(), &table_name);
        res
    }

    fn get_build_prefix_info(&self) -> Result<(Arc<DbLink>,String,MySqlArguments,Vec<String>)>{
        let tables = self.get_table_names();
        if tables.is_err() {
            return Err(Error::data_is_empty("get_table_names failed"));
        }
        let tables = tables.unwrap();
        if tables.is_empty() {
            return Err(Error::data_is_empty("get_table_names tables is empty"));
        }
        let db_link = self.get_db_handle();
        if db_link.is_err() {
            return Err(Error::data_is_empty("get_db_handle failed"));
        }
        let db_link = db_link.unwrap();
        let maker = self.get_sql_maker();
        if maker.is_err() {
            return Err(Error::data_is_empty("get_sql_maker failed"));
        }
        let maker = maker.unwrap();        
        let sql = maker.get_sql();
        let args = convert_list_bind_value_to_args(maker.get_bind().unwrap_or_default());
        Ok((db_link,sql,args,tables))
    }

    fn build_executor(self) -> Result<Self::Executor>{ 
        let (db_link,sql_tpl,args,tables) = self.get_build_prefix_info()?;
        if tables.len() != 1{
            return Err(Error::data_is_empty("build_executor from TableX but table_names not eq 1"));
        }
        let table = format!("{}",tables.get(0).unwrap());
        let mut executor = Self::Executor::with();
        let real_sql = Self::get_real_sql(sql_tpl,table);
        executor.set_db_handle(db_link).set_sql(real_sql).set_args(args);    
        Ok(executor)
    } 

    fn build_executors(self) -> Result<Vec<Self::Executor>>{ 
        let (db_link,sql_tpl,args,tables) = self.get_build_prefix_info()?;
        let mut res = vec![];
        for table in tables {
            let mut executor = Self::Executor::with();
            let real_sql = Self::get_real_sql(sql_tpl.clone(),table);
            executor.set_db_handle(db_link.clone()).set_sql(real_sql).set_args(args.clone());
            res.push(executor);
        }
        Ok(res)
    } 

}

#[macro_export]
macro_rules! lavender_default_tablex_property {
    () => {
        type Model = $crate::db::model::BaseModel;
        type Executor = $crate::db::table::ExecutorBase;
        fn with_sql_maker_list(ids: Vec<i64>,maker:$crate::db::query_statement::SqlMakerRaw) -> Self{
            let mut tables = std::collections::HashSet::new();
            for id in ids{
                let table = Self::get_table_name_by_index(id);
                tables.insert(table);
            }
            let mut t = tables.into_iter().collect::<Vec<_>>();
            t.sort();
            Self{
                sql_maker:Some(maker),
                tables:Some(t),
            }
        }

        fn get_table_names(&self) -> $crate::error::Result<Vec<String>>{
            if self.tables.is_none(){
                return Err($crate::error::Error::data_is_empty("no table set in tablex"));
            }
            Ok(self.tables.clone().unwrap())
        }    
 
        fn get_sql_maker(&self) -> $crate::error::Result<$crate::db::query_statement::SqlMakerRaw> {
            if self.sql_maker.is_none() {
                return Err($crate::error::Error::data_is_empty("sql maker not set or set error"));
            }
            Ok(self.sql_maker.clone().unwrap())
        }  
    };
}

// pub struct BaseTableX{
//     pub sql_maker: Option<SqlMakerRaw>,
//     pub tables:Option<Vec<String>>,
// }

// impl TableX for BaseTableX{
//     const TABLE_PREFIX_NAME:&'static str = "";
//     const DATABASE_PREFIX_NAME:&'static str ="";

//     const DATABASE_NAME:&'static str = "";

//     const DATABASE_NUM:i16 = 10 ;
//     const TABLE_NUM:i16 = 10 ;
//     lavender_default_tablex_property!();  
// }
